package com.cardone.platform.authority.service;

import com.cardone.common.dto.PaginationDto;
import com.cardone.context.Attributes;
import com.cardone.context.CodeException;
import com.cardone.context.ContextHolder;
import com.cardone.platform.authority.dao.*;
import com.cardone.platform.authority.dto.NavigationDto;
import com.cardone.platform.authority.dto.PermissionDto;
import com.cardone.platform.authority.dto.RoleDto;
import com.cardone.platform.authority.dto.RolePermissionDto;
import com.cardone.platform.common.service.EntityCodeService;
import com.cardone.platform.configuration.util.DictionaryUtils;
import com.cardone.platform.usercenter.dto.UserDto;
import com.cardone.platform.usercenter.service.UserService;
import com.cardone.platform.usercenter.util.UserUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * 角色服务
 *
 * @author yaohaitao
 */
@Getter
@Setter
@Transactional(readOnly = true)
@lombok.extern.slf4j.Slf4j
public class RoleDefaultService extends com.cardone.common.service.SimpleDefaultService<com.cardone.platform.authority.dto.RoleDto> implements RoleService {
    @Override
    public RoleDao getDao() {
        return ContextHolder.getBean(RoleDao.class);
    }

    @Override
    @Transactional
    public int deleteByIds(final String ids) {
        int deleteCount = ContextHolder.getBean(RoleDao.class).deleteByIds(ids);

        deleteCount += ContextHolder.getBean(RolePermissionDao.class).deleteByRoleIds(ids);

        deleteCount += ContextHolder.getBean(RoleUserDao.class).deleteByRoleIds(ids);

        deleteCount += ContextHolder.getBean(UsGroupRoleDao.class).deleteByRoleIds(ids);

        return deleteCount;
    }

    @Override
    public PaginationDto<Map<String, Object>> paginationByLikeName(Map<String, Object> model) {
        if (SecurityUtils.getSubject().hasRole("admin")) {
            model.put("isAdmin", true);
        } else {
            model.put("isAdmin", false);
            model.put("orgId", UserUtils.find().getOrgId());
        }

        return this.getDao().paginationByLikeName(model);
    }

    @Override
    @Transactional
    public int deleteById(String id) {
        return this.getDao().deleteById(id);
    }

    @Override
    @Transactional
    public String saveForNavigations(Map<String, Object> saveRoleMap) {
        String id = MapUtils.getString(saveRoleMap, "id");
        String code = ContextHolder.getBean(EntityCodeService.class).generateCode("ROLE");
        String name = MapUtils.getString(saveRoleMap, "name");
        String orgId;

        UserDto user = UserUtils.find();

        if (SecurityUtils.getSubject().hasRole("admin")) {
            orgId = MapUtils.getString(saveRoleMap, "orgId");
        } else {
            orgId = user.getOrgId();
        }

        if (StringUtils.isNotBlank(id)) {
            this.getDao().updateName(id, orgId, name);
        } else {
            id = UUID.randomUUID().toString();

            RoleDto saveRole = new RoleDto();

            saveRole.setId(id);
            saveRole.setCode(code);
            saveRole.setName(name);
            saveRole.setOrgId(orgId);

            this.getDao().saveByIdOrCode(saveRole);
        }

        List<NavigationDto> navigationList = ContextHolder.getBean(NavigationDao.class).findListBySiteId(NavigationDto.class, user.getSiteId());

        if (org.apache.commons.collections.CollectionUtils.isEmpty(navigationList)) {
            return id;
        }

        Object navigationIdListObject = MapUtils.getObject(saveRoleMap, "navigations");

        List<String> navigationIdList;

        if (navigationIdListObject == null) {
            navigationIdList = Lists.newArrayList();
        } else {
            navigationIdList = (List<String>) navigationIdListObject;
        }

        String typeId = DictionaryUtils.readIdByTypeCodeAndCode("permission_type", "urlBlackList");

        for (NavigationDto navigation : navigationList) {
            if (org.apache.commons.collections.CollectionUtils.isEmpty(navigationIdList) || !navigationIdList.contains(navigation.getId())) {
                Map<String, Object> deleteRolePermission = Maps.newHashMap();

                deleteRolePermission.put("roleId", id);
                deleteRolePermission.put("siteId", user.getSiteId());
                deleteRolePermission.put("permissionCode", navigation.getCode());

                ContextHolder.getBean(RolePermissionDao.class).deleteBySiteIdAndPermissionCode(deleteRolePermission);

                continue;
            }

            PermissionDto insertRermission = new PermissionDto();

            insertRermission.setSiteId(user.getSiteId());
            insertRermission.setTypeId(typeId);
            insertRermission.setCode(navigation.getCode());
            insertRermission.setName(navigation.getName());

            PermissionDto savedPermission = ContextHolder.getBean(PermissionDao.class).saveByIdOrCode(PermissionDto.class, insertRermission);

            RolePermissionDto rolePermission = new RolePermissionDto();

            rolePermission.setPermissionId(savedPermission.getId());
            rolePermission.setPermissionCode(savedPermission.getCode());
            rolePermission.setRoleId(id);

            ContextHolder.getBean(RolePermissionDao.class).saveByIdOrCode(rolePermission);
        }

        return id;
    }

    @Override
    @Transactional
    public String saveForPermissionIds(Map<String, Object> saveRoleMap) {
        String id = MapUtils.getString(saveRoleMap, "id");
        String code = ContextHolder.getBean(EntityCodeService.class).generateCode("ROLE");
        String name = MapUtils.getString(saveRoleMap, "name");
        String orgId;

        UserDto user = UserUtils.find();

        if (SecurityUtils.getSubject().hasRole("admin")) {
            orgId = MapUtils.getString(saveRoleMap, "orgId");
        } else {
            orgId = user.getOrgId();
        }

        if (StringUtils.isNotBlank(id)) {
            this.getDao().updateName(id, orgId, name);
        } else {
            id = UUID.randomUUID().toString();

            RoleDto saveRole = new RoleDto();

            saveRole.setId(id);
            saveRole.setCode(code);
            saveRole.setName(name);
            saveRole.setOrgId(orgId);

            this.getDao().saveByIdOrCode(saveRole);
        }

        List<String> sitePermissionIdList = ContextHolder.getBean(PermissionDao.class).readIdListBySiteIdAndTypeCode(user.getSiteId(), null);

        if (CollectionUtils.isEmpty(sitePermissionIdList)) {
            return id;
        }

        Object permissionIdListObject = MapUtils.getObject(saveRoleMap, "permissionIds");

        if (permissionIdListObject == null) {
            return id;
        }

        List<String> insertPermissionIdList = (List<String>) permissionIdListObject;

        for (String sitePermissionId : sitePermissionIdList) {
            if (CollectionUtils.isEmpty(insertPermissionIdList) || !insertPermissionIdList.contains(sitePermissionId)) {
                Map<String, Object> deleteRolePermission = Maps.newHashMap();

                deleteRolePermission.put("roleId", id);
                deleteRolePermission.put("permissionId", sitePermissionId);

                ContextHolder.getBean(RolePermissionDao.class).deleteByCode(deleteRolePermission);

                continue;
            }

            RolePermissionDto rolePermission = new RolePermissionDto();

            rolePermission.setPermissionId(sitePermissionId);
            rolePermission.setRoleId(id);

            ContextHolder.getBean(RolePermissionDao.class).saveByIdOrCode(rolePermission);
        }

        return id;
    }

    @Override
    @Transactional
    public <P> P saveForPermissionIds(final Class<P> mappedClass, final RoleDto saveRole) {
        final P p = this.saveByIdOrCode(mappedClass, saveRole);

        String roleId = null;

        try {
            roleId = BeanUtils.getProperty(p, Attributes.id.name());
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            RoleDefaultService.log.error(e.getMessage(), e);
        }

        ContextHolder.getBean(RolePermissionDao.class).saveRoleIdAndPermissionIds(roleId, saveRole.getPermissionIds());

        return p;
    }

    @Override
    @Transactional(readOnly = true)
    public <P> List<P> findListByUserId(final Class<P> mappedClass, final String userId) {
        return ContextHolder.getBean(RoleDao.class).findListByUserId(mappedClass, userId);
    }

    @Override
    public <P> List<P> findListByUserIdForOrg(Class<P> mappedClass, String userId) {
        UserDto user = ContextHolder.getBean(UserService.class).findById(userId);

        if (user == null) {
            throw new CodeException("用户不存在");
        }

        return ContextHolder.getBean(RoleDao.class).findListByUserIdForOrg(mappedClass, userId, user.getOrgId());
    }

    @Override
    @Transactional(readOnly = true)
    public String readIdByCode(final RoleDto readRole) {
        return ContextHolder.getBean(RoleDao.class).readIdByCode(readRole);
    }

    @Override
    @Transactional(readOnly = true)
    public List<String> readIdListByUserId(final String userId) {
        final List<RoleDto> roleList = ContextHolder.getBean(RoleService.class).findListByUserId(RoleDto.class, userId);

        final List<String> idList = Lists.newArrayList();

        if (CollectionUtils.isEmpty(roleList)) {
            return idList;
        }

        for (final RoleDto role : roleList) {
            idList.add(role.getId());
        }

        return idList;
    }
}
